package ru.susu.algebra.centralunits.exponent;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import ru.susu.algebra.centralunits.alternating.AlternatingCentralUnitsUtils;
import ru.susu.algebra.chartable.constructor.AlternatingCharTableUtil;
import ru.susu.algebra.field.quadratic.ExpEvaluateOperation;
import ru.susu.algebra.number.ClearSquaresOperation;
import ru.susu.algebra.pair.Pair;
import ru.susu.algebra.partition.Partition;
import ru.susu.algebra.ui.formatters.FactorizationFormatter;
import ru.susu.algebra.util.CollectionUtils;
import ru.susu.algebra.util.NumberUtilities;

/**
 * @author akargapolov
 * @since: 15.07.2010
 */
public class ExponentUtils
{
	private static final Logger _log = Logger.getLogger("exponent");

	public static BigInteger evaluateK(Partition rowPartition)
	{
		Partition colPartition = AlternatingCharTableUtil.getColumn4SpecialRow(rowPartition);
		BigInteger k = BigInteger.valueOf(1);
		for (Integer i : colPartition.getArrayRepresentation())
			k = k.multiply(BigInteger.valueOf(i));
		k = new ClearSquaresOperation().performOperation(k);		
		return k;
	}
	
	public static List<Pair<Integer, BigInteger>> evaluateExponent(Partition rowPartition)
	{
		BigInteger zx = AlternatingCharTableUtil.calcZX(rowPartition);
		Partition colPartition = AlternatingCharTableUtil.getColumn4SpecialRow(rowPartition);
		if (!AlternatingCentralUnitsUtils.isGoodCharacterForUnit(rowPartition, colPartition))
		{
			return Lists.newArrayList();
		}
		ExpEvaluateOperation expOp = new ExpEvaluateOperation();
		FactorizationFormatter formatter = new FactorizationFormatter();
		BigInteger k = evaluateK(rowPartition);
		

		List<Pair<Integer, BigInteger>> list = Lists.newArrayList();
		BigInteger result = BigInteger.ONE;

		for (Pair<Integer, Integer> pair : NumberUtilities.factorization(zx))
		{
			BigInteger p = BigInteger.valueOf(pair.getKey());
			BigInteger exp = expOp.performOperation(zx, p, k);
			list.add(Pair.pair(pair.getKey(), exp));
			_log.info("exp(p = " + p + ") = " + exp + " = " + formatter.format(exp));
			BigInteger gcd = result.gcd(exp);
			result = result.divide(gcd).multiply(exp);
		}

		_log.info("Exp(Z = " + zx + ", K = " + k + ") = " + result + " = " + formatter.format(result));

		return list;
	}

	public static List<BigInteger> evaluateExponents(List<Partition> partitions)
	{
		return Lists.transform(partitions, new Function<Partition, BigInteger>()
		{
			@Override
			public BigInteger apply(Partition from)
			{
				return getExponent(evaluateExponent(from));
			}
		});
	}

	public static Map<Partition, List<Pair<Integer, BigInteger>>> partition2Exponent(List<Partition> partitions)
	{
		Map<Partition, List<Pair<Integer, BigInteger>>> map = Maps.newLinkedHashMap();
		for (Partition partition : partitions)
		{
			map.put(partition, evaluateExponent(partition));
		}
		return map;
	}

	public static BigInteger getExponent(List<Pair<Integer, BigInteger>> exponents)
	{
		return NumberUtilities.lcm(CollectionUtils.getValues(exponents));
	}
	
	public static BigInteger getExponent(Partition rowPartition)
	{
		return getExponent(evaluateExponent(rowPartition));
	}
}
